#include "Ogre.h"
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#  include "OgreD3D9Plugin.h"
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
#  include "OgreGLES2Plugin.h"
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
#  include "OgreGLES2Plugin.h"
#endif
#include "OgreParticleFXPlugin.h"
#include "OgreRTShaderSystem.h"
#include "OgreMemoryTracker.h"

#include "GameEngine.h"
#include "Actor.h"

class CEngineFrameListener : public Ogre::FrameListener, public Ogre::WindowEventListener
{
public:
	CEngineFrameListener()
	{
		mClosed = false;
	}

	~CEngineFrameListener()
	{
	}

	virtual bool frameStarted(const Ogre::FrameEvent& evt)
    {
		CActor::tickAll(evt.timeSinceLastFrame);
		return true;
	}

	virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt)
    {
		return true;
	}

	virtual bool frameEnded(const Ogre::FrameEvent& evt)
	{
		return !mClosed;
	}

	virtual void windowMoved(Ogre::RenderWindow* rw)
	{
	}

	virtual void windowResized(Ogre::RenderWindow* rw)
	{
		CGameEngine::getSingletonPtr()->getCamera()->setAspectRatio(Ogre::Real(rw->getWidth()) / Ogre::Real(rw->getHeight()));
	}

	virtual bool windowClosing(Ogre::RenderWindow* rw)
	{
		return true;
	}

	virtual void windowClosed(Ogre::RenderWindow* rw)
	{
		mClosed = true;
	}

	virtual void windowFocusChange(Ogre::RenderWindow* rw)
	{
	}

private:
	bool mClosed;
};

static CGameEngine* gGameEngine = NULL;

CGameEngine* CGameEngine::getSingletonPtr()
{
	return gGameEngine;
}

CGameEngine::CGameEngine()
{
	gGameEngine = this;
	mFrameListener = new CEngineFrameListener();
}

CGameEngine::~CGameEngine()
{
	delete mFrameListener;
	gGameEngine = NULL;
}

bool CGameEngine::initEngine(const char* gamename)
{
	// init path
	initPath(gamename);

	// create ogre root
	mRoot = OGRE_NEW Ogre::Root(Ogre::StringUtil::BLANK, Ogre::StringUtil::BLANK, mLogDir);
	mRoot->addFrameListener(mFrameListener);

	// install plugins
	installPlugins();

	// init rtshader
	Ogre::RTShader::ShaderGenerator::initialize();
	Ogre::RTShader::ShaderGenerator* mShaderGenerator = Ogre::RTShader::ShaderGenerator::getSingletonPtr();
	mShaderGenerator->setShaderCachePath(mShaderPath);

	// set shader cache path
	FILE* inFile = fopen(mShaderCachePath.c_str(), "rb");
	if(inFile)
	{
		Ogre::GpuProgramManager::getSingleton().setSaveMicrocodesToCache(true);
		Ogre::DataStreamPtr istream(new Ogre::FileHandleDataStream(mShaderCachePath.c_str(), inFile, Ogre::DataStream::READ));
		Ogre::GpuProgramManager::getSingleton().loadMicrocodeCache(istream);
	}

	// create render window
	Ogre::RenderSystem* rs = mRoot->getAvailableRenderers()[0];
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
	rs->setConfigOption("Full Screen", "No");
#endif
	mRoot->setRenderSystem(mRoot->getAvailableRenderers()[0]);
	mWindow = mRoot->initialise(true, gamename);
	Ogre::WindowEventUtilities::addWindowEventListener(mWindow, mFrameListener);

	// create scene manager
	mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC, "SC_ROOT");
	Ogre::RTShader::ShaderGenerator::getSingletonPtr()->addSceneManager(mSceneMgr);

	// create camera
	mCamera = mSceneMgr->createCamera("PlayerCam");
	mCamera->setPosition(Ogre::Vector3(0,0,500));
	mCamera->lookAt(Ogre::Vector3(0,0,-300));
	mCamera->setNearClipDistance(5);
	mCamera->setAspectRatio(Ogre::Real(mWindow->getWidth()) / Ogre::Real(mWindow->getHeight()));

	// set view port
	Ogre::Viewport* vp = mWindow->addViewport(mCamera);
	vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

	return true;
}

void CGameEngine::initResource()
{
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void CGameEngine::finiEngine()
{
	Ogre::RTShader::ShaderGenerator::finalize();

	Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, mFrameListener);

	OGRE_DELETE mRoot;
	OGRE_DELETE mRenderPlugin;
	OGRE_DELETE mParticleFXPlugin;
}

void CGameEngine::addResourceLocation(const char* path, const char* rtype, const char* group, bool download)
{
	Ogre::String fullpath;
	if(download)
	{
		fullpath = mDownloadContentDir + path;
	}
	else
	{
		fullpath = mContentDir + path;
	}

	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(fullpath, rtype, group?group:Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}

void CGameEngine::initPath(const char* gamename)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
	char path[MAX_PATH];
	GetModuleFileName(NULL, path, sizeof(path));
	char* loc = strrchr(path, '\\')+1;
	sprintf(loc, "..\\%s\\Content\\", gamename);
	mContentDir = path;
	sprintf(loc, "..\\%s\\Download\\", gamename);
	mDownloadContentDir = path;
	sprintf(loc, "..\\%s\\%s.log", gamename, gamename, GetCurrentProcessId());
	mLogDir = path;
	sprintf(loc, "..\\%s\\ShaderCache.bin", gamename);
	mShaderCachePath = path;
	sprintf(loc, "..\\%s\\Shader\\", gamename);
	mShaderPath = path;
#if OGRE_MEMORY_TRACKER
	sprintf(loc, "..\\%s\\Leaks_%d.log", gamename, GetCurrentProcessId());
	Ogre::MemoryTracker::get().setReportFileName(path);
#endif
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
#endif
}

void CGameEngine::installPlugins()
{
	mParticleFXPlugin = OGRE_NEW Ogre::ParticleFXPlugin();
	mRoot->installPlugin(mParticleFXPlugin);
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
	mRenderPlugin = OGRE_NEW Ogre::D3D9Plugin();
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
	mRenderPlugin = OGRE_NEW GLES2Plugin();
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
	mRenderPlugin = OGRE_NEW GLES2Plugin();
#endif
	mRoot->installPlugin(mRenderPlugin);
}
